﻿#region License statement
// NJamb, a specification and delta-specification DSL
// Copyright (c) 2010-2011, Mark Knell
// Published under the MIT License; all other rights reserved
#endregion

#region using...
using System;
using Stile.Patterns.SelfDescribingPredicates;
#endregion

namespace NJamb.Specifications.Delta
{
	public abstract class DeltaSpecification {}

	public abstract class DeltaSpecification<TSubject, TEvaluation> : DeltaSpecification,
		IDeltaSpecification
		where TEvaluation : class, IEvaluation
	{
		protected DeltaSpecification(IEvaluable<TEvaluation> before)
		{
			Before = before;
		}

		public abstract IEvaluable<TEvaluation> After { get; }
		public IEvaluable<TEvaluation> Before { get; private set; }

		public IPreconditionEvaluation EvaluatePrecondition()
		{
			return new PreconditionEvaluation<TEvaluation>(GetTypedEvaluation(), After);
		}

		private TEvaluation GetTypedEvaluation()
		{
			TEvaluation evaluation = Before.Evaluate();
			InspectFirstEvaluation(evaluation);
			return evaluation;
		}

		protected virtual void InspectFirstEvaluation(TEvaluation evaluation) {}
	}

	public class DeltaSpecification<TSubject> : DeltaSpecification<TSubject, IEvaluation<TSubject>>
	{
		private readonly Func<TSubject, IEvaluable<IEvaluation<TSubject>>> _afterFactory;
		private TSubject _resultBefore;

		public DeltaSpecification(IEvaluable<IEvaluation<TSubject>> before, IEvaluable<IEvaluation<TSubject>> after)
			: this(before, x => after) {}

		public DeltaSpecification(IEvaluable<IEvaluation<TSubject>> before,
			Func<TSubject, IEvaluable<IEvaluation<TSubject>>> afterFactory)
			: base(before)
		{
			_afterFactory = afterFactory;
		}

		public override IEvaluable<IEvaluation<TSubject>> After
		{
			get { return _afterFactory.Invoke(_resultBefore); }
		}

		protected override void InspectFirstEvaluation(IEvaluation<TSubject> evaluation)
		{
			_resultBefore = evaluation.Result;
		}
	}
}
